using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Instedd.Labs.Geo.Library.GPS;
using System.Data;
using Instedd.Labs.Geo.Library.Kml;
using System.Data.Common;

namespace Instedd.Labs.Forms.Library
{
    internal class SurveyLocationResolver
    {
        private Database db;
        private IGeoCoder geoCoder;

        public SurveyLocationResolver(Database db, IGeoCoder geoCoder)
        {
            Guard.ArgumentNotNull(db, "db");
            Guard.ArgumentNotNull(geoCoder, "geoCoder");

            this.db = db;
            this.geoCoder = geoCoder;
        }

        public void ResolveAllUnlocatedSurveys()
        {
            Resolve(GetUnlocatedSurveys());
        }

        const string ResetLocationResolutionAttemptsBySurveyIdSQL = "UPDATE [Forms] SET locationResolutionAttempts = 0 WHERE [id] = @id AND ([latitude] IS NULL OR [longitude] IS NULL)";

        public void ResetLocationResolutionAttemps(string surveyId)
        {
            using (DbCommand command = db.GetSqlStringCommand(ResetLocationResolutionAttemptsBySurveyIdSQL))
            {
                db.AddInParameter(command, "id", DbType.String, surveyId);
                db.ExecuteNonQuery(command);
            }
 
        }

        internal void Resolve(Dictionary<string, Address> surveys)
        {
            foreach (KeyValuePair<string, Address> pair in surveys)
            {
                Resolve(pair.Key, this.geoCoder.GetLatLongFromPostalAddress(GetPostalAddress(pair.Value)));
            }
        }

        internal string GetPostalAddress(Address address)
        {
            StringBuilder queryBuilder = new StringBuilder();
            string[] addressParts = new string[] { address.StreetAddress, address.City, address.State, address.PostalCode };

            for (int partIndex = 0; partIndex < addressParts.Length && !string.IsNullOrEmpty(addressParts[partIndex]); partIndex++)
            {
                if (queryBuilder.Length != 0)
                {
                    queryBuilder.Append(",");
                }

                queryBuilder.Append(addressParts[partIndex]);
            }

            return queryBuilder.ToString();
        }

        const string UpdateLocationSurveySQL = "UPDATE [Forms] SET latitude = @latitude, longitude = @longitude, locationResolutionAttempts = locationResolutionAttempts + 1 WHERE [id] = @id";
        
        const string UnlocatedSurveysSQL = "SELECT [id], [streetAddress], [city], [postalCode], [state] FROM [Forms] WHERE ([latitude] IS NULL OR [longitude] IS NULL) AND [locationResolutionAttempts] = 0";

        internal Dictionary<string, Address> GetUnlocatedSurveys()
        {
            Dictionary<string, Address> result = new Dictionary<string, Address>();

            using (IDataReader reader = db.ExecuteReader(CommandType.Text, UnlocatedSurveysSQL))
            {
                while (reader.Read())
                {
                    Address address = new Address();
                    address.StreetAddress = reader["streetAddress"] as string;
                    address.State = reader["state"] as string;
                    address.PostalCode = reader["postalCode"] as string;
                    address.City = reader["city"] as string;

                    result.Add((string)reader["id"], address);
                }
            }

            return result;
        }

        internal int Resolve(string surveyId, LatLong location)
        {
            using (DbCommand command = db.GetSqlStringCommand(UpdateLocationSurveySQL))
            {
                db.AddInParameter(command, "id", DbType.String, surveyId);
                if (location != null)
                {
                    db.AddInParameter(command, "latitude", DbType.Double, location.Lat);
                    db.AddInParameter(command, "longitude", DbType.Double, location.Long);
                }
                else
                {
                    db.AddInParameter(command, "latitude", DbType.Double, DBNull.Value);
                    db.AddInParameter(command, "longitude", DbType.Double, DBNull.Value);
                }
                
                return db.ExecuteNonQuery(command);
            }
        }
    }
}